////////////// esp8266-sdd1306 lib
// https://github.com/ThingPulse/esp8266-oled-ssd1306
// https://github.com/cesanta/elk
// IR Connect

#include "elk.h"  // Add Elk library

extern "C" void _delay(int milli) { delay(milli); }
extern "C" void _write(int pin, int val) { digitalWrite(pin, val); }
extern "C" void _mode(int pin, int mode) { pinMode(pin, mode); }
extern "C" void _initSerial(int baud) { Serial.begin(baud); }
extern "C" void _writeSerial(const char *str) { Serial.println(str); }


struct js *js;

#define JS_SIZE 400 // Memory for the JS engine in bytes

/* 
8266     IR
----     ---
RX  -> TX
TX  -> RX
VV  -> VCC
GND -> GND
*/

// oled sdd1306
/* 
8266     1306
----     ---
3v  -> VCC
D1  -> SCL
D2  -> SDA
GND -> GND
*/

// dht11
/* 
8266     dht11
----     ---
3v  -> VCC
D4  -> OUT / Signal
GND -> GND
*/


// DhtEXPx
 
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

#include "time.h"

#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <uri/UriBraces.h>
#include <uri/UriRegex.h>
#include <ESP8266HTTPClient.h> 

#include "smartconfig.h"
#include "airkiss.h"

#define ADC_PIN         34 

#include <Wire.h>               // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h"        // legacy: #include "SSD1306.h"
SSD1306Wire display(0x3c, 4, 5, GEOMETRY_128_64);   // ADDRESS, SDA, SCL  -  SDA and SCL usually populate automatically based on your board's pins_arduino.h

//SoftSerial serial(14, 12); // RX, TX , D5, D6
#include "SoftwareSerial.h"
SoftwareSerial ss( 14, 12, false );  // RX, TX , D5, D6


 
#include <DHTesp.h>
DHTesp dht;
bool isDhtSetup = false;

int ir_cold = 1;
int ir_hot = 2;

int lastDp = -1;

int lastCheck = -1;
int duration = 15 * 60 * 1000; // switch duration, 15 minutes

bool wifiConnected = false;

int lastTemp = -1;

String _dhtRead(){ 

  if(!isDhtSetup){
    isDhtSetup = true;    
    dht.setup(2, DHTesp::DHT11); 
  }
  
  delay(dht.getMinimumSamplingPeriod());

  float humidity = dht.getHumidity();
  float temperature = dht.getTemperature();

  if( lastTemp > 0 ) {
    if( temperature<lastTemp && temperature<24.5){
      if( millis() > lastCheck + duration ){
        ir_send( ir_cold );
        lastCheck = millis();
      }
    }else if( temperature>lastTemp && temperature>26.5){ 
      if( millis() > lastCheck + duration ){
        ir_send( ir_hot );
        lastCheck = millis();
      }
    }
  }

  lastTemp = temperature;
   
  String ret =  String(temperature) +" C, " + String(humidity) + "%";

  return ret;
}





const char *hostname = "esp8266-ir-01";  


ESP8266WebServer server(80); 

// Replace with your network credentials
const char* ssid = "WZIOT";
const char* password = "77778888";

long timezone = -8;
byte daysavetime = 0;

char buff[512];
int vref = 1100;
 
void dp(String str){  
  display.clear();
  dp(0, 0, str);
}
 
void dp(int x, int y, String str){
  display.drawString(x, y, str);
  display.display();  
  lastDp = millis();
}
 
void dp(int y, String str){
  dp(0, y, str);
}

void _info( char* str){
  Serial.println(str);
}


extern "C" void ir_send(uint8_t t){  
    uint8_t buffer[2];
    buffer[0] = 0xe3;
    buffer[1] = t;
    ss.write(buffer, 2);
    ss.flush(); 
}

extern "C" void ir_test(){  
    uint8_t buffer[1];
    buffer[0] = 0xe4;
    ss.write(buffer, 1);
    ss.flush(); 
}

void startServer(){       
     
  server.on(UriBraces("/js/{}"), []() {
    _info("GET /js/{string}");
    const char* s = server.pathArg(0).c_str();
    js_eval(js, s, 0); 
    server.send(200, "text/plain", s);
  }); 
     
  server.on(UriBraces("/dht"), []() {
    _info("GET /dht"); 
    String v = _dhtRead();
    dp(v);
    server.send(200, "text/plain", v );
  });       
     
  server.on(UriBraces("/ir/duration/{}"), []() {
    _info("GET /ir/duration/{int}");
    duration = server.pathArg(0).toInt();
 
    server.send(200, "text/plain", String(duration));
  });   
     
  server.on(UriBraces("/ir/cold/{}"), []() {
    _info("GET /ir/cold/{int}");
    ir_cold = server.pathArg(0).toInt();
 
    server.send(200, "text/plain", String(ir_cold));
  });   
     
  server.on(UriBraces("/ir/hot/{}"), []() {
    _info("GET /ir/hot/{int}");
    ir_hot = server.pathArg(0).toInt();
 
    server.send(200, "text/plain", String(ir_hot));
  });
     
  server.on(UriBraces("/ir/off"), []() {
    _info("GET /ir/off");

    uint8_t buffer[1];
    buffer[0] = 0xe2;
    ss.write(buffer, 1);
    ss.flush(); 
 
    server.send(200, "text/plain", "ir/off");
  });       

  server.on(UriBraces("/ir/test"), []() {
    _info("GET /ir/test");

    uint8_t buffer[1];
    buffer[0] = 0xe4;
    ss.write(buffer, 1);
    ss.flush(); 
 
    server.send(200, "text/plain", "ir/test");
  });  
     
  server.on(UriBraces("/ir/study/{}"), []() {
    _info("GET /ir/study/{int}");
    uint8_t t = server.pathArg(0).toInt();

    uint8_t buffer[2];
    buffer[0] = 0xe0;
    buffer[1] = t;
    ss.write(buffer, 2);
    ss.flush(); 

    String v = String(t);
    server.send(200, "text/plain", v);
  });  
     
  server.on(UriBraces("/ir/send/{}"), []() {
    _info("GET /ir/send/{int}");
    uint8_t t = server.pathArg(0).toInt();

    uint8_t buffer[2];
    buffer[0] = 0xe3;
    buffer[1] = t;
    ss.write(buffer, 2);
    ss.flush(); 
    
    String v = String(t);
    server.send(200, "text/plain", v);
  });
           
   

  server.onNotFound([]() {
    server.send(404, "text/plain", "404 Not Found!");
  });

  server.on(UriBraces("/on/{}"), []() {    
    String p = server.pathArg(0);
    int pin = p.toInt();
    pinMode(pin, OUTPUT);
    digitalWrite(pin, HIGH);
    server.send(200, "text/plain", "set PIN on: " + p );
  });

  server.on(UriBraces("/off/{}"), []() {
    
    String p = server.pathArg(0);
    
    int pin = p.toInt();
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
    
    server.send(200, "text/plain", "set PIN off: " + p );
  });

  

  server.on(UriBraces("/analogRead/{}"), []() {
    
    _info("GET /analogRead/{pin}");
    
    String p = server.pathArg(0);
    
    int pin = p.toInt();
    uint16_t v = analogRead(pin); 
    
    server.send(200, "text/plain", String(v) );
    
  });

  
 
  
  server.begin();
//  Serial.println("HTTP server started");
   
} // start server

 
 

void printMillis() {
//  Serial.print(F("millis() = "));  // show that millis() isn't correct across most Sleep modes
//  Serial.println(millis());
//  Serial.flush();  // needs a Serial.flush() else it may not print the whole message before sleeping
} 
 
     



bool getLocalTime(struct tm * info, uint32_t ms) {
  uint32_t count = ms / 10;
  time_t now;

  time(&now);
  localtime_r(&now, info);

  if (info->tm_year > (2016 - 1900)) {
    return true;
  }

  while (count--) {
    delay(10);
    time(&now);
    localtime_r(&now, info);
    if (info->tm_year > (2016 - 1900)) {
      return true;
    }
  }
  return false;
}
  

bool autoConfig()
{
  dp("autoConfig now");
  WiFi.begin();
  for (int i = 0; i < 20; i++)
  {
    int wstatus = WiFi.status();
    if (wstatus == WL_CONNECTED)
    {
      Serial.println("AutoConfig Success");
      Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
      WiFi.printDiag(Serial);
      wifiConnected = true;
      return true;
      //break;
    }
    else
    {
//      Serial.print("AutoConfig Waiting......");
//      Serial.println(wstatus);
      delay(1000);
    }
  }
//  Serial.println("AutoConfig Faild!" );
  return false;
  //WiFi.printDiag(Serial);
}
void smartConfig()
{
  WiFi.mode(WIFI_STA);
//  Serial.println("\r\nWait for Smartconfig");

  
  dp( "Smart Config now");
  
  WiFi.setAutoConnect(true);  // 设置自动连接
  WiFi.beginSmartConfig();
  int i = 0;
  while ( i++ < 60 )  // wait for 1 minute
  {
//    Serial.print(".");
    if (WiFi.smartConfigDone())
    {
      Serial.println("SmartConfig Success");
      Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
      WiFi.setAutoConnect(true);  // 设置自动连接
      wifiConnected = true;
      break;
    }
    delay(1000); // 这个地方一定要加延时，否则极易崩溃重启
  }
}

void showHeap(){
  long  fh = ESP.getFreeHeap();
  char  fhc[20];

  ltoa(fh, fhc, 10);
  String freeHeap = String(fhc);
//  Serial.println("freeHeap: " + freeHeap);
} 
     


void setup() {

  Serial.begin(115200);
  ss.begin(115200);
//  Serial.println("Start");

  js = js_create(malloc(JS_SIZE), JS_SIZE);
  js_import(js, "delay", (uintptr_t) _delay, "vi");
  js_import(js, "digitalWrite", (uintptr_t) _write, "vii");
  js_import(js, "pinMode", (uintptr_t) _mode, "vii");
  js_import(js, "initSerial", (uintptr_t) _initSerial, "vi");
  js_import(js, "writeSerial", (uintptr_t) _writeSerial, "vs");
  js_import(js, "jsinfo", (uintptr_t) js_info, "sm");
  
  js_import(js, "ir_test", (uintptr_t) ir_test, "vii");
  js_import(js, "ir_send", (uintptr_t) ir_send, "vii");

  js_eval(js, "let ledPin = 13, ms = 100;", 0); // LedPin 13, blink interval 100ms
  js_eval(js, "ir_test();", 0); // LedPin 13, blink interval 100ms

  // Initialising the UI will init the display too.
  display.init();

  display.flipScreenVertically();
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  dp("Hello world");

  // test ir
  

//    uint8_t buffer[1];
//    buffer[0] = 0xe4;
//    ss.write(buffer, 1);    
//    ss.flush(); 
  
  showHeap();
//  
//  Serial.print(F("\nReset reason = "));
  String resetCause = ESP.getResetReason();
//  Serial.println(resetCause);
 

  WiFi.setAutoConnect(true); 
//
//
  if (!autoConfig())
  {
//    Serial.println("Start smartConfig module");
    smartConfig();
  }

  _info("WZIOT CONNECTED");
  _info("IP address: ");
//  Serial.println(WiFi.localIP());
//
//
//  Serial.printf("current hostname: %s\n", WiFi.hostname().c_str()); 
//  Serial.printf("update hostname to: ");
//  WiFi.hostname(hostname); 
//  Serial.printf("updated hostname: %s\n", WiFi.hostname().c_str());  
//
  if( wifiConnected ){
    
    configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
  //
    dp("wifi connected");
    dp(16,WiFi.localIP().toString().c_str());
    
    startServer();
  //
    // Hostname defaults to esp8266-[ChipID]
    ArduinoOTA.setHostname(hostname);
  
    ArduinoOTA.begin();
    
    _info("ArduinoOTA READY");
    
  }
 
  
}

int flag = 0;
void loop() {

  if( wifiConnected ){
    ArduinoOTA.handle();
    server.handleClient(); 
  }  

  if (millis() - flag > 5 * 60 * 1000) {
    flag = millis();
    dp(_dhtRead());
  }

  if ( millis() > lastDp + 10 * 1000 ){ // shutdown display after 10s
    display.clear();
    display.display();
    lastDp = millis() + 3600 * 1000; // extend 1 hour to avoid keep clearing
  }
  
}